package net.pimpas.xml.dom4j.serviceprovider;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import net.pimpas.core.configuration.exception.ConfigurationException;
import net.pimpas.core.serviceprovider.ServiceProviderAnnotation;
import net.pimpas.core.serviceprovider.instancetype.ServiceProviderInstanceType;
import net.pimpas.xml.dom4j.attribute.AttributeDom4jImpl;
import net.pimpas.xml.dom4j.node.NodeDom4j;
import net.pimpas.xml.dom4j.node.NodeDom4jImpl;
import net.pimpas.xml.exception.XmlAccessException;
import net.pimpas.xml.exception.XmlParseException;
import net.pimpas.xml.serviceprovider.XmlServiceProvider;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

@ServiceProviderAnnotation(
		instanceType=ServiceProviderInstanceType.NORMAL,
		serviceProviderType=XmlServiceProvider.class
)
public class Dom4jServiceProviderImpl implements Dom4jServiceProvider {

	private Document document;
	private boolean parsed;
	
	public Dom4jServiceProviderImpl() {
		this.setParsed(false);
	}

	public Document getDocument() {
		return document;
	}

	private void setDocument(Document document) {
		this.document = document;
	}

	public void parse(File arg0) throws XmlParseException {
		SAXReader saxReader = new SAXReader();
		try {
			this.setDocument(saxReader.read(arg0));
			this.setParsed(true);
		} catch (DocumentException e) {
			throw new XmlParseException(e);
		}
	}
	

	public void parse(InputStream arg0) throws XmlParseException {
		SAXReader saxReader = new SAXReader();
		try {
			this.setDocument(saxReader.read(arg0));
			this.setParsed(true);
		} catch (DocumentException e) {
			throw new XmlParseException(e);
		}
		
	}

	public void parse(Reader arg0) throws XmlParseException {
		SAXReader saxReader = new SAXReader();
		try {
			this.setDocument(saxReader.read(arg0));
			this.setParsed(true);
		} catch (DocumentException e) {
			throw new XmlParseException(e);
		}
	}

	public void parse(URL arg0) throws XmlParseException {
		SAXReader saxReader = new SAXReader();
		try {
			this.setDocument(saxReader.read(arg0));
			this.setParsed(true);
		} catch (DocumentException e) {
			throw new XmlParseException(e);
		}
	}

	private boolean isParsed() {
		return parsed;
	}

	private void setParsed(boolean parsed) {
		this.parsed = parsed;
	}

	public List<Node> selectNodes(String xPath) throws XmlAccessException {
		return this.selectNodes(this.getDocument(), xPath);
	}

	@SuppressWarnings("unchecked")
	public List<Node> selectNodes(Node n, String xPath) throws XmlAccessException {
		if(!this.isParsed()) throw new XmlAccessException("Parse not executed!");
		return n.selectNodes(xPath);
	}

	public List<net.pimpas.xml.node.Node> getChildrens(String arg0) throws XmlAccessException {
		return this.getChildrens(new NodeDom4jImpl(this.getDocument().getRootElement()), arg0);
	}
	
	private void getChildrens(List<net.pimpas.xml.node.Node> t, NodeDom4j n, String find) {
		if(n.getNode() instanceof Element) {
			Element el = (Element) n.getNode();
			for(int i=0;i<el.nodeCount();i++)
				if(el.node(i) instanceof Element)
					this.getChildrens(t, new NodeDom4jImpl(el.node(i)), find);
		}
		
		if(n.getName().equals(find))
			t.add(n);
	}

	public List<net.pimpas.xml.node.Node> getChildrens(net.pimpas.xml.node.Node arg0, String arg1) throws XmlAccessException {
		if(!this.isParsed()) throw new XmlAccessException("Parse not executed!");
		List<net.pimpas.xml.node.Node> t = new ArrayList<net.pimpas.xml.node.Node>();
		this.getChildrens(t, (NodeDom4j) arg0, arg1);
		return t;
	}

	public net.pimpas.xml.attribute.Attribute getAttribute(net.pimpas.xml.node.Node arg0, String arg1) throws XmlAccessException {
		NodeDom4j node = (NodeDom4j) arg0;
		if( !(node.getNode() instanceof Element)) return null;
		
		Element el = (Element) node.getNode();
		return new AttributeDom4jImpl(el.attribute(arg1));
	}

	@SuppressWarnings("unchecked")
	public List<net.pimpas.xml.attribute.Attribute> getAttributes(net.pimpas.xml.node.Node arg0) throws XmlAccessException {
		NodeDom4j node = (NodeDom4j) arg0;
		if( !(node.getNode() instanceof Element)) return null;
		
		Element el = (Element) node.getNode();
		return el.attributes();
	}

	public net.pimpas.xml.node.Node getFirstNode(net.pimpas.xml.node.Node arg0, String arg1) throws XmlAccessException {
		return this.getChildrens(arg0, arg1).get(0);
	}

	public void config() throws ConfigurationException {
		
	}

	public String print() throws XmlAccessException {
		StringWriter sw = new StringWriter();
		try {
			this.getDocument().write(sw);
			return sw.toString();
		} catch (IOException e) {
			throw new XmlAccessException(e);
		}
	}

	public boolean isTerminated() {
		return false;
	}
}